Exemplo: altura e peso
Pessoas mais altas tendem a ser mais pesadas?
Temos dados de \(80\) alunos e alunas, com alturas em cm e pesos em kg:
Correlação linear
Vamos subtrair, de cada peso, a média dos pesos; vamos subtrair, de cada altura, a média das alturas.
peso_altura <- peso_altura %>%
mutate(
altura_desvio = altura - mean(altura),
peso_desvio = peso - mean(peso)
)
O novo scatterplot tem a mesma forma. Só mudam as escalas:
peso_altura %>%
ggplot(aes(altura_desvio, peso_desvio)) +
geom_point() +
labs(
x = 'desvios altura (cm)',
y = 'desvios peso\n(kg)'
)

Quando o desvio da altura e o desvio do peso têm o mesmo sinal, os valores têm uma associação positiva: ou altura e peso estão ambos acima da média, ou ambos abaixo da média.
Colorindo os pontos de acordo com a associação:
peso_altura %>%
ggplot(aes(altura_desvio, peso_desvio)) +
geom_point(
data = . %>% filter(altura_desvio * peso_desvio >= 0),
color = 'blue'
) +
geom_point(
data = . %>% filter(altura_desvio * peso_desvio < 0),
color = 'red'
) +
geom_hline(aes(yintercept = 0), linetype = 'dashed') +
geom_vline(aes(xintercept = 0), linetype = 'dashed') +
labs(
x = 'desvios altura (cm)',
y = 'desvios peso\n(kg)'
)

Covariância
Examine o código acima: para verificar se os desvios têm o mesmo sinal, basta verificar se o produto dos desvios é positivo.
E mais: quanto maior o produto dos desvios, mais acima (ou mais abaixo) das respectivas médias os desvios estão, ao mesmo tempo. Ou seja, maior a associação entre as duas variáveis.
Estamos falando de
\[
\text{produto dos desvios}_i = (\text{altura}_i - E(\text{altura})) \cdot (\text{peso}_i - E(\text{peso}))
\]
Podemos calcular os produtos dos desvios e achar a média destes produtos. O resultado vai ser a covariância:
\[
\text{Cov}(\text{altura}, \text{peso}) =
\frac{
\sum\; [(\text{altura}_i - E(\text{altura})) \cdot (\text{peso}_i - E(\text{peso}))]
}{n}
\]
Ou, de forma mais compacta,
\[
\text{Cov}(\text{altura}, \text{peso}) =
E[(\text{altura} - E(\text{altura})) \cdot (\text{peso} - E(\text{peso}))]
\]
O R usa \(n - 1\) no denominador, em vez de \(n\). Isto significa que o R calcula a covariância amostral, que serve como um estimador da covariância populacional.
Calculando:
peso_altura %>%
mutate(produto = altura_desvio * peso_desvio) %>%
summarize(cov = sum(produto)/(nrow(.) - 1)) %>%
pull(cov)
## [1] 73,8935
Em R
cov(peso_altura$altura, peso_altura$peso)
## [1] 73,8935
Exercícios
Qual é a unidade da covariância? Examine as fórmulas acima para responder.
Execute o bloco abaixo com diversos valores de k, inclusive valores negativos.
k <- 10
X <- 1:5
Y <- k * X
tibble(X, Y) %>%
ggplot(aes(X, Y)) +
geom_point(size = 2) +
labs(
title = paste('cov(X, Y) =', cov(X, Y))
)

Qual a covariância máxima entre duas variáveis? E a mínima?
Usando as propriedades do valor esperado, mostre que, para quaisquer variáveis aleatórias \(X\) e \(Y\)
\[
\text{Cov}(X,Y) \quad=\quad E\big[(X - E(X)) \cdot (Y - E(Y))\big] \quad=\quad E(XY) - E(X)E(Y)
\]
Lembre-se de que a variância de uma variável aleatória \(X\) pode ser escrita como
\[
\text{Var}(X) = E(X^2) - [E(X)]^2
\]
Alguma semelhança entre esta expressão e a expressão do item anterior?
Qual a covariância de uma variável aleatória \(X\) com ela mesma?
O R usa \(n - 1\) no denominador da covariância. Mostre que definir a covariância como \[
\text{Cov}(X, Y) = \frac{1}{n - 1} \cdot \sum_{i = 1}^n \big[(x_i - E(X)) \cdot (y_i - E(Y)) \big]
\]
é equivalente a
\[
\text{Cov}(X, Y) = \frac{n}{n-1} \cdot \big[ E(XY) - E(X)E(Y) \big]
\]
Usando o data frame peso_altura, compute o valor da expressão acima e compare com o resultado de cov(peso_altura$altura, peso_altura$peso).
Coeficiente de correlação linear
Vamos padronizar as alturas e pesos (subtrair a média e dividir pelo desvio padrão):
peso_altura <- peso_altura %>%
mutate(
altura_padronizada = scale(altura),
peso_padronizado = scale(peso)
)
E construir o scatterplot:
peso_altura %>%
ggplot(aes(altura_padronizada, peso_padronizado)) +
geom_point(
data = . %>% filter(altura_padronizada * peso_padronizado >= 0),
color = 'blue'
) +
geom_point(
data = . %>% filter(altura_padronizada * peso_padronizado < 0),
color = 'red'
) +
geom_hline(aes(yintercept = 0), linetype = 'dashed') +
geom_vline(aes(xintercept = 0), linetype = 'dashed') +
labs(
x = 'altura padronizada',
y = 'peso\npadronizado'
)

Ou seja, agora as variáveis são
\[
Z_A = \frac{\text{altura} - E(\text{altura})}{DP(\text{altura})}
\]
e
\[
Z_P = \frac{\text{peso} - E(\text{peso})}{DP(\text{peso})}
\]
A covariância entre elas é
cov(peso_altura$peso_padronizado, peso_altura$altura_padronizada)
## [,1]
## [1,] 0,6440311
Quando padronizamos as duas variáveis, a covariância entre elas se chama correlação.
O coeficiente de correlação amostral é este valor.
\[
r = \frac{\sum x_i y_i}{n - 1}
\]
onde \(X\) e \(Y\) são variáveis padronizadas.
Alguns livros chamam o coeficiente de correlação de \(\rho\) em vez de \(r\).
O coeficiente de correlação sempre está entre \(-1\) e \(1\), inclusive.
Fórmulas alternativas para o coeficiente de correlação entre \(X\) e \(Y\) (não necessariamente padronizadas):
\[
r = \frac{\text{Cov}(X, Y)}{DP_X \cdot DP_Y}
\]
e
\[
r = \frac{
\sum(x_i - E(X))\cdot(y_i - E(Y))
}{
\sqrt{\sum(x_i - E(X))^2\cdot(y_i - E(Y))^2}
}
\]
Em R
cor(peso_altura$peso, peso_altura$altura)
## [1] 0,6440311
Exercícios
Qual a unidade do coeficiente de correlação?
Execute o bloco abaixo com diversos valores de k, inclusive valores negativos.
k <- 10
X <- 1:5
Y <- k * X
tibble(X, Y) %>%
ggplot(aes(X, Y)) +
geom_point(size = 2) +
labs(
title = paste('cor(X, Y) =', cor(X, Y))
)

Como você explica os valores de \(\text{cor}(X,Y)\) no item anterior, em comparação com os valores de \(\text{cov}(X,Y)\) nesse outro exercício?
Qual a correlação de uma variável aleatória \(X\) com ela mesma?
Usando o data frame peso_altura, compute o valor do coeficiente de correlação entre peso e altura usando as fórmulas alternativas.
Observações importantes
A correlação é linear

Se \(r > 0\), valores altos de uma variável tendem a corresponder a valores altos da outra variável.
Se \(r < 0\), valores altos de uma variável tendem a corresponder a valores baixos da outra variável.
Se \(r = 0\), não há correlação linear entre as variáveis.
O coeficiente de correlação \(r\) só mede a correlação linear entre duas variáveis.

Correlação \(\neq\) causação
Dados coletados em Oldenburg, na Alemanha, nos anos \(1930\), mostrando a correlação entre a quantidade de cegonhas e a população (de pessoas) na cidade:
df <- read_csv('data/Storks.csv') %>%
transmute(
cegonhas = Storks,
pessoas = Population
)
r <- cor(df$cegonhas, df$pessoas) %>% round(2)
df %>%
ggplot(aes(cegonhas, pessoas)) +
geom_point() +
labs(
title = paste('r =', r)
)

Variável oculta: quantidade de casas com chaminé.
Ou coincidência: http://tylervigen.com/spurious-correlations
Teste e intervalo de confiança para a correlação
Exemplo: PIB e CO\(_2\)
Em uma amostra de \(10\) países, examinamos o PIB (em trilhões de dólares) e a quantidade de emissões de CO\(_2\) (em milhões de toneladas):
Gráfico:
df %>%
ggplot(aes(PIB, emissões)) +
geom_point() +
scale_y_continuous(
'emissões\n(milhões de\ntoneladas)',
breaks = seq(0, 1200, 200),
limits = c(0, 1200)
) +
scale_x_continuous(
'PIB (trilhões US$)',
breaks = 0:6,
limits = c(0, 6)
)

O coeficiente de correlação amostral é
## [1] 0,9115924
Como esta é uma amostra, devemos calcular um intervalo de confiança para o coeficiente de correlação populacional.
O R faz isto com a função cor.test:
ct <- cor.test(df$PIB, df$emissões)
ct
##
## Pearson's product-moment correlation
##
## data: df$PIB and df$emissões
## t = 6,272, df = 8, p-value = 0,0002399
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## 0,6618340 0,9791965
## sample estimates:
## cor
## 0,9115924
Conclusão: com \(95\%\) de confiança, estimamos que o coeficiente de correlação populacional é capturado pelo intervalo
\[
[\quad 0{,}661834 \quad ;\quad 0{,}9791965 \quad]
\]
Exercícios
Qual é o tipo de teste usado por cor.test?
Quais são as hipóteses do teste?
Como é calculado o valor da estatística do teste (\(6{,}272\) no exemplo acima)?
Como é calculado o valor \(p\) (\(0{,}0002399\) no exemplo acima)?
Implemente uma função em R para fazer o mesmo que cor.test.
LS0tCnRpdGxlOiAnQ29ycmVsYcOnw6NvIGVudHJlIGR1YXMgdmFyacOhdmVpcycKc3VidGl0bGU6ICdQcm9iZXN0JwphdXRob3I6ICdmbmF1ZmVsJwplbWFpbDogJ2h0dHBzOi8vZm5hdWZlbC5naXRodWIuaW8vJwpkYXRlOiAnICh2LiBgciBmb3JtYXQoU3lzLkRhdGUoKSwgIiVkLyVtLyVZIilgKScKbGFuZzogJ3B0JwojIFRPRE86IGluY2x1ZGUgTGFUZVggZmllbGRzCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiByZWFkYWJsZQogICAgICAgICAgICMgaHR0cHM6Ly9ib290c3dhdGNoLmNvbS8zL3JlYWRhYmxlLwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgY3NzOiBzdHlsZXMuY3NzCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgIyB0b2NfZmxvYXQ6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgZmlnX2NhcHRpb246IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgc2VsZl9jb250YWluZWQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgICMgY29kZV9mb2xkaW5nOiBzaG93CiAgICAjIGtlZXBfbWQ6IHRydWUKICAgICMgaW5jbHVkZXM6CiAgICAjICAgaW5faGVhZGVyOiBoZWFkZXIuaHRtbAogICAgIyAgIGJlZm9yZV9ib2R5OiBkb2NfcHJlZml4Lmh0bWwKICAgICMgICBhZnRlcl9ib2R5OiBkb2Nfc3VmZml4Lmh0bWwKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShrbml0cikKCm9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBUUlVFLCAKICAjIGNvbGxhcHNlID0gVFJVRSwKICAjIGNhY2hlID0gVFJVRSwKICBvdXQud2lkdGggPSAiNzUlIiwKICBmaWcuYWxpZ24gPSAnY2VudGVyJywKICBmaWcud2lkdGggPSA2LAogIGZpZy5zaG93ID0gImhvbGQiCikKCm9wdGlvbnMoZHBseXIucHJpbnRfbWluID0gNiwgZHBseXIucHJpbnRfbWF4ID0gNikKCiMgU3VwcmVzcyBjcmF5b24gb3V0cHV0Cm9wdGlvbnMoY3JheW9uLmVuYWJsZWQgPSBGQUxTRSkKCiMgVXNlZnVsIGxpYnJhcmllcwpsaWJyYXJ5KGdsdWUpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGxhdGV4MmV4cCkKbGlicmFyeShrYWJsZUV4dHJhKQoKIyBGb3IgbmljZSBkYXRhZnJhbWUgc3VtbWFyaWVzCmxpYnJhcnkoc3VtbWFyeXRvb2xzKQpzdF9vcHRpb25zKAogIHBsYWluLmFzY2lpID0gRkFMU0UsCiAgZGZTdW1tYXJ5LnZhcm51bWJlcnMgPSBGQUxTRSwKICBkZlN1bW1hcnkuc3R5bGUgPSAnZ3JpZCcsCiAgZGZTdW1tYXJ5LmdyYXBoLm1hZ25pZiA9IC43NQopCgojIFRpZHkhCmxpYnJhcnkodGlkeXZlcnNlKQoKIyBTb2JlciB0aGVtZSBmb3IgZ2dwbG90CnRoZW1lX3NldCgKICB0aGVtZV9saW5lZHJhdygpICsgICAgICAgICAgICAgICAgICAgICAgICAgIyBTZXQgc2ltcGxlIHRoZW1lIGZvciBnZ3Bsb3QKICAgIHRoZW1lKCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB3aXRoIHNvbWUgdHdlYWtzCiAgICAgIGF4aXMudGl0bGUueS5sZWZ0ID0gZWxlbWVudF90ZXh0KAogICAgICAgICBhbmdsZSA9IDAsICAgICAgICAgICAgICAgICAgICAgICAgICAjIE5ldmVyIHJvdGF0ZSB5IGF4aXMgdGl0bGUKICAgICAgICAgbWFyZ2luID0gbWFyZ2luKHIgPSAyMCksICAgICAgICAgICAgIyBTZXBhcmF0ZSB5IGF4aXMgdGl0bGUgYSBsaXR0bGUgCiAgICAgICAgIHZqdXN0ID0gLjUgICAgICAgICAgICAgICAgICAgICAgICAgICMgTGVhdmUgeSBheGlzIHRpdGxlIGluIHRoZSBtaWRkbGUKICAgICAgKSwKICAgICAgYXhpcy50aXRsZS54LmJvdHRvbSA9IGVsZW1lbnRfdGV4dCgKICAgICAgICAgbWFyZ2luID0gbWFyZ2luKHQgPSAyMCkgICAgICAgICAgICAgIyBTZXBhcmF0ZSB4IGF4aXMgdGl0bGUgYSBsaXR0bGUgCiAgICAgICksCiAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwgICAgICAgICAgICMgTm8gYXhpcyBsaW5lcwogICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksICAgICAgICAjIE5vIGZyYW1lCiAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkgICAgICMgTm8gZ3JpZCBtaW5vciBsaW5lcwogICAgKQopCgojIEF2b2lkIHNjaWVudGlmaWMgbm90YXRpb24gYW5kIHVzZSBhIGNvbW1hIGFzIGRlY2ltYWwgc2VwYXJhdG9yCm9wdGlvbnMoCiAgc2NpcGVuID0gMTUsCiAgT3V0RGVjID0gJywnCikKCiMgRm9ybWF0IGEgbnVtYmVyIHdpdGggdGhvdXNhbmQgc2VwYXJhdG9ycyAoZGVmYXVsdCBwb2ludCkKIyBhbmQgZGVjaW1hbCBjb21tYSBlbmNsb3NlZCBpbiBjdXJseSBicmFjZXMgZm9yIExhVGVYIHByaW50aW5nLgojIENBUkVGVUw6IGlmIGNhbGxlZCBvdXRzaWRlIG1hdGggbW9kZSwgd2lsbCBwcmludCB0aGUgYnJhY2VzIQpmbSA8LSBmdW5jdGlvbih4LCBiaWcgPSAnLicsIGRlY2ltYWwgPSAneyx9JywgLi4uKSB7CiAgaWYgKCFpcy5udW1lcmljKHgpKSB7CiAgICB4CiAgfSBlbHNlIHsKICAgIHByZXR0eU51bSh4LCBiaWcubWFyayA9IGJpZywgZGVjaW1hbC5tYXJrID0gZGVjaW1hbCwgLi4uKQogIH0KCn0KCiMgU2V0IHRoaXMgYXMgYSBob29rIGZvciBpbmxpbmUgUiBjb2RlCmtuaXRyOjprbml0X2hvb2tzJHNldChpbmxpbmUgPSBmbSkKCgojIFRvIGNlbnRlciB0aGUgcmVzdWx0cyBvZiBhIGNodW5rIChpbWFnZSwgdmlkZW8gZXRjLikKIyBVc2FnZTogCiMgICAgICAgICBvdXQuZXh0cmE9Y2VudGVyKCkKIyAgICAgICAgIApjZW50ZXIgPC0gZnVuY3Rpb24oKXsKICAKICBpZiAoaXNfaHRtbF9vdXRwdXQoKSkgewogICAgJ2NsYXNzPSJjZW50ZXIiJwogIH0KICAKfQoKCiMgVG8gZW1iZWQgWVQgdmlkZW9zIGluIEhUTUwgYW5kIHRoZSBsaW5rIChjZW50ZXJlZCkgaW4gTGFUZVgKZW1iZWRfeXQgPC0gZnVuY3Rpb24oY29kZSkgewoKICBpZiAoaXNfaHRtbF9vdXRwdXQoKSkgewogICAgaW5jbHVkZV91cmwoCiAgICAgIHBhc3RlMCgKICAgICAgICAnaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvJywKICAgICAgICBjb2RlCiAgICAgICkKICAgICkKICB9IGVsc2UgewogICAgY2F0KAogICAgICBwYXN0ZTAoCiAgICAgICAgJ2BgYHs9bGF0ZXh9XG4nLAogICAgICAgICdcXGJlZ2lue2NlbnRlcn0gXFx1cmx7aHR0cHM6Ly95b3V0dS5iZS8nLAogICAgICAgIGNvZGUsCiAgICAgICAgJ30gXFxlbmR7Y2VudGVyfVxuJywKICAgICAgICAnYGBgJwogICAgICApCiAgICApCiAgfQogIAp9CgpgYGAKCmBgYHtqcyBqYXZhc2NyaXB0LWluaXQsIGVjaG89RkFMU0V9CgovLyBNYWtlIG9mZi1zaXRlIGxpbmtzIG9wZW4gaW4gYSBuZXcgd2luZG93L3RhYgpmdW5jdGlvbiBjaGFuZ2VUYXJnZXRzKCkgewogICQoImEiKS5hdHRyKAogICAgInRhcmdldCIsIGZ1bmN0aW9uKCkgewogICAgICAvLyBMb2FkIGxvY2FsIGxpbmtzIGxvY2FsbHkKICAgICAgaWYgKHRoaXMuaG9zdCA9PSBsb2NhdGlvbi5ob3N0KSByZXR1cm4gIl9zZWxmIgogICAgICAvLyBMb2FkIG9mZi1zaXRlIGxpbmtzIGluIGEgbmV3IHdpbmRvdwogICAgICBlbHNlIHJldHVybiAiX2JsYW5rIjsKICAgIH0KICApOwp9CgovLyBFeGVjdXRlIHdoZW4gZG9jdW1lbnQgaXMgcmVhZHkKJCgKICBjaGFuZ2VUYXJnZXRzCikKYGBgCgoKIyBFeGVtcGxvOiBhbHR1cmEgZSBwZXNvCgpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjYWNoZT1UUlVFfQpwZXNvX2FsdHVyYSA8LSByZWFkX2NzdignZGF0YS9IZWlnaHRzX2FuZF9XZWlnaHRzLmNzdicpICU+JSAKICBtdXRhdGUoCiAgICBhbHR1cmEgPSBIZWlnaHQgKiAyLjU0LAogICAgcGVzbyA9IFdlaWdodCAqIDAuNDUKICApCmBgYAoKOjo6IHsucm1kYm94fQoKUGVzc29hcyBtYWlzIGFsdGFzIHRlbmRlbSBhIHNlciBtYWlzIHBlc2FkYXM/CgpUZW1vcyBkYWRvcyBkZSAkYHIgbnJvdyhwZXNvX2FsdHVyYSlgJCBhbHVub3MgZSBhbHVuYXMsIGNvbSBhbHR1cmFzIGVtIGNtIGUgcGVzb3MgZW0ga2c6CgpgYGB7ciBlY2hvPUZBTFNFfQpwZXNvX2FsdHVyYSAlPiUgc2VsZWN0KGFsdHVyYSwgcGVzbykKYGBgCgo6OjoKCiogTyBncsOhZmljbyBkZSBkaXNwZXJzw6NvICgqc2NhdHRlcnBsb3QqKSDDqSBvIGlkZWFsIHBhcmEgdmlzdWFsaXphciBhIGNvcnJlbGHDp8OjbyBlbnRyZSBkdWFzIHZhcmnDoXZlaXMgbnVtw6lyaWNhczoKCiAgICBgYGB7cn0KICAgIHBlc29fYWx0dXJhICU+JSAKICAgICAgZ2dwbG90KGFlcyhhbHR1cmEsIHBlc28pKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBsYWJzKAogICAgICAgICAgeCA9ICdhbHR1cmEgKGNtKScsCiAgICAgICAgICB5ID0gJ3Blc29cbihrZyknCiAgICAgICAgKQogICAgYGBgCgoKIyBDb3JyZWxhw6fDo28gbGluZWFyCgoqIFZhbW9zIHN1YnRyYWlyLCBkZSBjYWRhIHBlc28sIGEgbcOpZGlhIGRvcyBwZXNvczsgdmFtb3Mgc3VidHJhaXIsIGRlIGNhZGEgYWx0dXJhLCBhIG3DqWRpYSBkYXMgYWx0dXJhcy4KCiAgICBgYGB7cn0KICAgIHBlc29fYWx0dXJhIDwtIHBlc29fYWx0dXJhICU+JSAKICAgICAgbXV0YXRlKAogICAgICAgIGFsdHVyYV9kZXN2aW8gPSBhbHR1cmEgLSBtZWFuKGFsdHVyYSksCiAgICAgICAgcGVzb19kZXN2aW8gPSBwZXNvIC0gbWVhbihwZXNvKQogICAgICApCiAgICBgYGAKCiogTyBub3ZvICpzY2F0dGVycGxvdCogdGVtIGEgbWVzbWEgZm9ybWEuIFPDsyBtdWRhbSBhcyBlc2NhbGFzOgoKICAgIGBgYHtyfQogICAgcGVzb19hbHR1cmEgJT4lIAogICAgICBnZ3Bsb3QoYWVzKGFsdHVyYV9kZXN2aW8sIHBlc29fZGVzdmlvKSkgKwogICAgICAgIGdlb21fcG9pbnQoKSArCiAgICAgICAgbGFicygKICAgICAgICAgIHggPSAnZGVzdmlvcyBhbHR1cmEgKGNtKScsCiAgICAgICAgICB5ID0gJ2Rlc3Zpb3MgcGVzb1xuKGtnKScKICAgICAgICApCiAgICBgYGAKCiogUXVhbmRvIG8gZGVzdmlvIGRhIGFsdHVyYSBlIG8gZGVzdmlvIGRvIHBlc28gdMOqbSBvIFttZXNtbyBzaW5hbF17LmhsfSwgb3MgdmFsb3JlcyB0w6ptIHVtYSBbYXNzb2NpYcOnw6NvIHBvc2l0aXZhXXsuaGx9OiBvdSBhbHR1cmEgZSBwZXNvIGVzdMOjbyBhbWJvcyBhY2ltYSBkYSBtw6lkaWEsIG91IGFtYm9zIGFiYWl4byBkYSBtw6lkaWEuCgoqIENvbG9yaW5kbyBvcyBwb250b3MgZGUgYWNvcmRvIGNvbSBhIGFzc29jaWHDp8OjbzoKCiAgICBgYGB7cn0KICAgIHBlc29fYWx0dXJhICU+JSAKICAgICAgZ2dwbG90KGFlcyhhbHR1cmFfZGVzdmlvLCBwZXNvX2Rlc3ZpbykpICsKICAgICAgICBnZW9tX3BvaW50KAogICAgICAgICAgZGF0YSA9IC4gJT4lIGZpbHRlcihhbHR1cmFfZGVzdmlvICogcGVzb19kZXN2aW8gPj0gMCksCiAgICAgICAgICBjb2xvciA9ICdibHVlJwogICAgICAgICkgKwogICAgICAgIGdlb21fcG9pbnQoCiAgICAgICAgICBkYXRhID0gLiAlPiUgZmlsdGVyKGFsdHVyYV9kZXN2aW8gKiBwZXNvX2Rlc3ZpbyA8IDApLAogICAgICAgICAgY29sb3IgPSAncmVkJwogICAgICAgICkgKwogICAgICAgIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAwKSwgbGluZXR5cGUgPSAnZGFzaGVkJykgKwogICAgICAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAwKSwgbGluZXR5cGUgPSAnZGFzaGVkJykgKwogICAgICAgIGxhYnMoCiAgICAgICAgICB4ID0gJ2Rlc3Zpb3MgYWx0dXJhIChjbSknLAogICAgICAgICAgeSA9ICdkZXN2aW9zIHBlc29cbihrZyknCiAgICAgICAgKQogICAgYGBgCgojIENvdmFyacOibmNpYQoKKiBFeGFtaW5lIG8gY8OzZGlnbyBhY2ltYTogcGFyYSB2ZXJpZmljYXIgc2Ugb3MgZGVzdmlvcyB0w6ptIG8gbWVzbW8gc2luYWwsIGJhc3RhIHZlcmlmaWNhciBzZSBvIHByb2R1dG8gZG9zIGRlc3Zpb3Mgw6kgcG9zaXRpdm8uCgoqIEUgbWFpczogcXVhbnRvIG1haW9yIG8gcHJvZHV0byBkb3MgZGVzdmlvcywgbWFpcyBhY2ltYSAob3UgbWFpcyBhYmFpeG8pIGRhcyByZXNwZWN0aXZhcyBtw6lkaWFzIG9zIGRlc3Zpb3MgZXN0w6NvLCBbYW8gbWVzbW8gdGVtcG9dey5obH0uIE91IHNlamEsIG1haW9yIGEgYXNzb2NpYcOnw6NvIGVudHJlIGFzIGR1YXMgdmFyacOhdmVpcy4KCiogRXN0YW1vcyBmYWxhbmRvIGRlCgogICQkCiAgXHRleHR7cHJvZHV0byBkb3MgZGVzdmlvc31faSA9IChcdGV4dHthbHR1cmF9X2kgLSBFKFx0ZXh0e2FsdHVyYX0pKSBcY2RvdCAoXHRleHR7cGVzb31faSAtIEUoXHRleHR7cGVzb30pKQogICQkCgoqIFBvZGVtb3MgY2FsY3VsYXIgb3MgcHJvZHV0b3MgZG9zIGRlc3Zpb3MgZSBhY2hhciBhIG3DqWRpYSBkZXN0ZXMgcHJvZHV0b3MuIE8gcmVzdWx0YWRvIHZhaSBzZXIgYSBbY292YXJpw6JuY2lhOl17LmhsfQoKICAkJAogIFx0ZXh0e0Nvdn0oXHRleHR7YWx0dXJhfSwgXHRleHR7cGVzb30pID0gCiAgXGZyYWN7CiAgXHN1bVw7IFsoXHRleHR7YWx0dXJhfV9pIC0gRShcdGV4dHthbHR1cmF9KSkgXGNkb3QgKFx0ZXh0e3Blc299X2kgLSBFKFx0ZXh0e3Blc299KSldCiAgfXtufQogICQkCgoqIE91LCBkZSBmb3JtYSBtYWlzIGNvbXBhY3RhLAoKICAkJAogIFx0ZXh0e0Nvdn0oXHRleHR7YWx0dXJhfSwgXHRleHR7cGVzb30pID0gCiAgRVsoXHRleHR7YWx0dXJhfSAtIEUoXHRleHR7YWx0dXJhfSkpIFxjZG90IChcdGV4dHtwZXNvfSAtIEUoXHRleHR7cGVzb30pKV0KICAkJAoKKiBbTyBSIHVzYSAkbiAtIDEkIG5vIGRlbm9taW5hZG9yLF17LmhsfSBlbSB2ZXogZGUgJG4kLiBJc3RvIHNpZ25pZmljYSBxdWUgbyBSIGNhbGN1bGEgYSBbY292YXJpw6JuY2lhIGFtb3N0cmFsXXsuaGx9LCBxdWUgc2VydmUgY29tbyB1bSBlc3RpbWFkb3IgZGEgY292YXJpw6JuY2lhIHBvcHVsYWNpb25hbC4KCiogQ2FsY3VsYW5kbzoKCiAgICBgYGB7cn0KICAgIHBlc29fYWx0dXJhICU+JSAKICAgICAgbXV0YXRlKHByb2R1dG8gPSBhbHR1cmFfZGVzdmlvICogcGVzb19kZXN2aW8pICU+JSAKICAgICAgc3VtbWFyaXplKGNvdiA9IHN1bShwcm9kdXRvKS8obnJvdyguKSAtIDEpKSAlPiUgCiAgICAgIHB1bGwoY292KQogICAgYGBgCgoKIyMgRW0gUgoKYGBge3J9CmNvdihwZXNvX2FsdHVyYSRhbHR1cmEsIHBlc29fYWx0dXJhJHBlc28pCmBgYAoKIyMgRXhlcmPDrWNpb3MKCiogUXVhbCDDqSBhIHVuaWRhZGUgZGEgY292YXJpw6JuY2lhPyBFeGFtaW5lIGFzIGbDs3JtdWxhcyBhY2ltYSBwYXJhIHJlc3BvbmRlci4KCiogW117I2V4Y292fSBFeGVjdXRlIG8gYmxvY28gYWJhaXhvIGNvbSBkaXZlcnNvcyB2YWxvcmVzIGRlIGBrYCwgaW5jbHVzaXZlIHZhbG9yZXMgbmVnYXRpdm9zLgoKICAgIGBgYHtyfQogICAgayA8LSAxMAogICAgCiAgICBYIDwtIDE6NQogICAgWSA8LSBrICogWAogICAgCiAgICB0aWJibGUoWCwgWSkgJT4lIAogICAgICBnZ3Bsb3QoYWVzKFgsIFkpKSArCiAgICAgICAgZ2VvbV9wb2ludChzaXplID0gMikgKwogICAgICAgIGxhYnMoCiAgICAgICAgICB0aXRsZSA9IHBhc3RlKCdjb3YoWCwgWSkgPScsIGNvdihYLCBZKSkKICAgICAgICApCiAgICBgYGAKCiogUXVhbCBhIGNvdmFyacOibmNpYSBtw6F4aW1hIGVudHJlIGR1YXMgdmFyacOhdmVpcz8gRSBhIG3DrW5pbWE/CgoqIFVzYW5kbyBhcyBwcm9wcmllZGFkZXMgZG8gdmFsb3IgZXNwZXJhZG8sIG1vc3RyZSBxdWUsIHBhcmEgcXVhaXNxdWVyIHZhcmnDoXZlaXMgYWxlYXTDs3JpYXMgJFgkIGUgJFkkCgogICQkCiAgXHRleHR7Q292fShYLFkpIFxxdWFkPVxxdWFkIEVcYmlnWyhYIC0gRShYKSkgXGNkb3QgKFkgLSBFKFkpKVxiaWddIFxxdWFkPVxxdWFkIEUoWFkpIC0gRShYKUUoWSkKICAkJAoKKiBMZW1icmUtc2UgZGUgcXVlIGEgdmFyacOibmNpYSBkZSB1bWEgdmFyacOhdmVsIGFsZWF0w7NyaWEgJFgkIHBvZGUgc2VyIGVzY3JpdGEgY29tbwoKICAkJAogIFx0ZXh0e1Zhcn0oWCkgPSBFKFheMikgLSBbRShYKV1eMgogICQkCgogIEFsZ3VtYSBzZW1lbGhhbsOnYSBlbnRyZSBlc3RhIGV4cHJlc3PDo28gZSBhIGV4cHJlc3PDo28gZG8gaXRlbSBhbnRlcmlvcj8KICAKKiBRdWFsIGEgY292YXJpw6JuY2lhIGRlIHVtYSB2YXJpw6F2ZWwgYWxlYXTDs3JpYSAkWCQgY29tIGVsYSBtZXNtYT8KCiogTyBSIHVzYSAkbiAtIDEkIG5vIGRlbm9taW5hZG9yIGRhIGNvdmFyacOibmNpYS4gTW9zdHJlIHF1ZSBkZWZpbmlyIGEgY292YXJpw6JuY2lhIGNvbW8gCiAgJCQKICBcdGV4dHtDb3Z9KFgsIFkpID0gXGZyYWN7MX17biAtIDF9IFxjZG90IFxzdW1fe2kgPSAxfV5uIFxiaWdbKHhfaSAtIEUoWCkpIFxjZG90ICh5X2kgLSBFKFkpKSBcYmlnXQogICQkCgogIMOpIGVxdWl2YWxlbnRlIGEKICAKICAkJAogIFx0ZXh0e0Nvdn0oWCwgWSkgPSBcZnJhY3tufXtuLTF9IFxjZG90IFxiaWdbIEUoWFkpIC0gRShYKUUoWSkgXGJpZ10KICAkJAoKCiogVXNhbmRvIG8gKmRhdGEgZnJhbWUqIGBwZXNvX2FsdHVyYWAsIGNvbXB1dGUgbyB2YWxvciBkYSBleHByZXNzw6NvIGFjaW1hIGUgY29tcGFyZSBjb20gbyByZXN1bHRhZG8gZGUgYGNvdihwZXNvX2FsdHVyYSRhbHR1cmEsIHBlc29fYWx0dXJhJHBlc28pYC4KCgojIENvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbyBsaW5lYXIKCiogVmFtb3MgcGFkcm9uaXphciBhcyBhbHR1cmFzIGUgcGVzb3MgKHN1YnRyYWlyIGEgbcOpZGlhIGUgZGl2aWRpciBwZWxvIGRlc3ZpbyBwYWRyw6NvKToKCiAgICBgYGB7cn0KICAgIHBlc29fYWx0dXJhIDwtIHBlc29fYWx0dXJhICU+JSAKICAgICAgbXV0YXRlKAogICAgICAgIGFsdHVyYV9wYWRyb25pemFkYSA9IHNjYWxlKGFsdHVyYSksCiAgICAgICAgcGVzb19wYWRyb25pemFkbyA9IHNjYWxlKHBlc28pCiAgICAgICkKICAgIGBgYAoKKiBFIGNvbnN0cnVpciBvICpzY2F0dGVycGxvdCo6CgogICAgYGBge3J9CiAgICBwZXNvX2FsdHVyYSAlPiUgCiAgICAgIGdncGxvdChhZXMoYWx0dXJhX3BhZHJvbml6YWRhLCBwZXNvX3BhZHJvbml6YWRvKSkgKwogICAgICAgIGdlb21fcG9pbnQoCiAgICAgICAgICBkYXRhID0gLiAlPiUgZmlsdGVyKGFsdHVyYV9wYWRyb25pemFkYSAqIHBlc29fcGFkcm9uaXphZG8gPj0gMCksCiAgICAgICAgICBjb2xvciA9ICdibHVlJwogICAgICAgICkgKwogICAgICAgIGdlb21fcG9pbnQoCiAgICAgICAgICBkYXRhID0gLiAlPiUgZmlsdGVyKGFsdHVyYV9wYWRyb25pemFkYSAqIHBlc29fcGFkcm9uaXphZG8gPCAwKSwKICAgICAgICAgIGNvbG9yID0gJ3JlZCcKICAgICAgICApICsKICAgICAgICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMCksIGxpbmV0eXBlID0gJ2Rhc2hlZCcpICsKICAgICAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMCksIGxpbmV0eXBlID0gJ2Rhc2hlZCcpICsKICAgICAgICBsYWJzKAogICAgICAgICAgeCA9ICdhbHR1cmEgcGFkcm9uaXphZGEnLAogICAgICAgICAgeSA9ICdwZXNvXG5wYWRyb25pemFkbycKICAgICAgICApCiAgICBgYGAKCiogT3Ugc2VqYSwgYWdvcmEgYXMgdmFyacOhdmVpcyBzw6NvCgogICQkCiAgWl9BID0gXGZyYWN7XHRleHR7YWx0dXJhfSAtIEUoXHRleHR7YWx0dXJhfSl9e0RQKFx0ZXh0e2FsdHVyYX0pfQogICQkCgogIGUKICAKICAkJAogIFpfUCA9IFxmcmFje1x0ZXh0e3Blc299IC0gRShcdGV4dHtwZXNvfSl9e0RQKFx0ZXh0e3Blc299KX0KICAkJAoKKiBBIGNvdmFyacOibmNpYSBlbnRyZSBlbGFzIMOpCgogICAgYGBge3J9CiAgICBjb3YocGVzb19hbHR1cmEkcGVzb19wYWRyb25pemFkbywgcGVzb19hbHR1cmEkYWx0dXJhX3BhZHJvbml6YWRhKQogICAgYGBgCgoqIFF1YW5kbyBwYWRyb25pemFtb3MgYXMgZHVhcyB2YXJpw6F2ZWlzLCBhIGNvdmFyacOibmNpYSBlbnRyZSBlbGFzIHNlIGNoYW1hIFtjb3JyZWxhw6fDo29dey5obH0uCgoqIE8gW2NvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbyBhbW9zdHJhbF17LmhsfSDDqSBlc3RlIHZhbG9yLgoKICAkJAogIHIgPSBcZnJhY3tcc3VtIHhfaSB5X2l9e24gLSAxfQogICQkCgogIG9uZGUgWyRYJCBlICRZJCBzw6NvIHZhcmnDoXZlaXMgcGFkcm9uaXphZGFzXXsuaGx9LgogIAoqIEFsZ3VucyBsaXZyb3MgY2hhbWFtIG8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvIGRlICRccmhvJCBlbSB2ZXogZGUgJHIkLgoKKiBPIGNvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbyBbc2VtcHJlIGVzdMOhIGVudHJlICQtMSQgZSAkMSRdey5obH0sIGluY2x1c2l2ZS4KCiogRsOzcm11bGFzIGFsdGVybmF0aXZhcyBwYXJhIG8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvIGVudHJlICRYJCBlICRZJCAobsOjbyBuZWNlc3NhcmlhbWVudGUgcGFkcm9uaXphZGFzKToKCiAgJCQKICByID0gXGZyYWN7XHRleHR7Q292fShYLCBZKX17RFBfWCBcY2RvdCBEUF9ZfQogICQkCiAgCiAgZQoKICAkJAogIHIgPSBcZnJhY3sKICAgIFxzdW0oeF9pIC0gRShYKSlcY2RvdCh5X2kgLSBFKFkpKQogIH17CiAgICBcc3FydHtcc3VtKHhfaSAtIEUoWCkpXjJcY2RvdCh5X2kgLSBFKFkpKV4yfQogIH0KICAkJAoKCiMjIEVtIFIKCmBgYHtyfQpjb3IocGVzb19hbHR1cmEkcGVzbywgcGVzb19hbHR1cmEkYWx0dXJhKQpgYGAKCiMjIEV4ZXJjw61jaW9zCgoqIFF1YWwgYSB1bmlkYWRlIGRvIGNvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8Ojbz8KCiogRXhlY3V0ZSBvIGJsb2NvIGFiYWl4byBjb20gZGl2ZXJzb3MgdmFsb3JlcyBkZSBga2AsIGluY2x1c2l2ZSB2YWxvcmVzIG5lZ2F0aXZvcy4KCiAgICBgYGB7cn0KICAgIGsgPC0gMTAKICAgIAogICAgWCA8LSAxOjUKICAgIFkgPC0gayAqIFgKICAgIAogICAgdGliYmxlKFgsIFkpICU+JSAKICAgICAgZ2dwbG90KGFlcyhYLCBZKSkgKwogICAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICAgICAgICBsYWJzKAogICAgICAgICAgdGl0bGUgPSBwYXN0ZSgnY29yKFgsIFkpID0nLCBjb3IoWCwgWSkpCiAgICAgICAgKQogICAgYGBgCgoqIENvbW8gdm9jw6ogZXhwbGljYSBvcyB2YWxvcmVzIGRlICRcdGV4dHtjb3J9KFgsWSkkIG5vIGl0ZW0gYW50ZXJpb3IsIGVtIGNvbXBhcmHDp8OjbyBjb20gb3MgdmFsb3JlcyBkZSAkXHRleHR7Y292fShYLFkpJCBbbmVzc2Ugb3V0cm8gZXhlcmPDrWNpb10oI2V4Y292KT8KCiogUXVhbCBhIGNvcnJlbGHDp8OjbyBkZSB1bWEgdmFyacOhdmVsIGFsZWF0w7NyaWEgJFgkIGNvbSBlbGEgbWVzbWE/CgoqIFVzYW5kbyBvICpkYXRhIGZyYW1lKiBgcGVzb19hbHR1cmFgLCBjb21wdXRlIG8gdmFsb3IgZG8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvIGVudHJlIHBlc28gZSBhbHR1cmEgdXNhbmRvIGFzIGbDs3JtdWxhcyBhbHRlcm5hdGl2YXMuCgoKIyMgT2JzZXJ2YcOnw7VlcyBpbXBvcnRhbnRlcwoKIyMjIEEgY29ycmVsYcOnw6NvIMOpICpsaW5lYXIqIHstfQoKIVtdKGltYWdlcy9QZWFyc29uX0NvcnJlbGF0aW9uX0NvZWZmaWNpZW50X2FuZF9hc3NvY2lhdGVkX3NjYXR0ZXJwbG90cy5wbmcpeyBzdHlsZT0id2lkdGg6IDkwJTsiIC5jZW50ZXIgfQoKOjo6e3N0eWxlPSJ0ZXh0LWFsaWduOiByaWdodDsifQooaHR0cHM6Ly9jb21tb25zLndpa2ltZWRpYS5vcmcvd2lraS9GaWxlOlBlYXJzb25fQ29ycmVsYXRpb25fQ29lZmZpY2llbnRfYW5kX2Fzc29jaWF0ZWRfc2NhdHRlcnBsb3RzLnBuZykKOjo6CgoqIFNlICRyID4gMCQsIHZhbG9yZXMgW2FsdG9zXXsuaGx9IGRlIHVtYSB2YXJpw6F2ZWwgdGVuZGVtIGEgY29ycmVzcG9uZGVyIGEgdmFsb3JlcyBbYWx0b3Ndey5obH0gZGEgb3V0cmEgdmFyacOhdmVsLgoKKiBTZSAkciA8IDAkLCB2YWxvcmVzIFthbHRvc117LmhsfSBkZSB1bWEgdmFyacOhdmVsIHRlbmRlbSBhIGNvcnJlc3BvbmRlciBhIHZhbG9yZXMgW2JhaXhvc117LmhsfSBkYSBvdXRyYSB2YXJpw6F2ZWwuCgoqIFNlICRyID0gMCQsIG7Do28gaMOhIGNvcnJlbGHDp8OjbyBbbGluZWFyXXsuaGx9IGVudHJlIGFzIHZhcmnDoXZlaXMuCgoqIE8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvICRyJCBzw7MgbWVkZSBhIGNvcnJlbGHDp8OjbyBbbGluZWFyXXsuaGx9IGVudHJlIGR1YXMgdmFyacOhdmVpcy4KCiAgIVtdKGltYWdlcy9Db3JyZWxhdGlvbl9leGFtcGxlczIuc3ZnKXsgc3R5bGU9IndpZHRoOiA5MCU7IiAuY2VudGVyIH0KCjo6OntzdHlsZT0idGV4dC1hbGlnbjogcmlnaHQ7In0KKGh0dHBzOi8vY29tbW9ucy53aWtpbWVkaWEub3JnL3dpa2kvRmlsZTpDb3JyZWxhdGlvbl9leGFtcGxlczIuc3ZnKQo6OjoKCiogW0V4ZW1wbG9zIGRlIEFuc2NvbWJlXShodHRwczovL3B0Lndpa2lwZWRpYS5vcmcvd2lraS9RdWFydGV0b19kZV9BbnNjb21iZSksIGNvbSAkNCQgY29uanVudG9zIGRlIGRhZG9zIGNvbSBbbyBtZXNtbyB2YWxvciBkZSAkciRdey5obH0gbWFzIGNvbSBhc3NvY2lhw6fDtWVzIFttdWl0byBkaWZlcmVudGVzXXsuaGx9IGVudHJlIGFzIHZhcmnDoXZlaXM6CgogIGBgYHtyfQogIGFuc2NvbWJlCiAgYGBgCgogICAgYGBge3IgbWVzc2FnZT1GQUxTRSwgY2FjaGU9VFJVRX0KICAgIGRlc2VuaGFyIDwtIGZ1bmN0aW9uKG4pIHsKICAgICAgCiAgICAgIG5vbWV4IDwtIHBhc3RlMCgneCcsIG4pCiAgICAgIG5vbWV5IDwtIHBhc3RlMCgneScsIG4pCiAgICAgIHIgPC0gY29yKGFuc2NvbWJlW1tub21leF1dLCBhbnNjb21iZVtbbm9tZXldXSkgJT4lIHJvdW5kKDIpCiAgICAgIAogICAgICBhbnNjb21iZSAlPiUgCiAgICAgICAgZ2dwbG90KGFlcyguZGF0YVtbbm9tZXhdXSwgLmRhdGFbW25vbWV5XV0pKSArCiAgICAgICAgICBnZW9tX3BvaW50KCkgKwogICAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJywgc2UgPSBGQUxTRSkgKwogICAgICAgICAgbGFicygKICAgICAgICAgICAgdGl0bGUgPSBwYXN0ZSgnciA9JywgcikKICAgICAgICAgICkKICAgICAgCiAgICB9CiAgICAgIAogICAgICAKICAgIHBsb3RzIDwtIG1hcCgxOjQsIGRlc2VuaGFyKQogICAgcGxvdHNbWzFdXSArIHBsb3RzW1syXV0gKyBwbG90c1tbM11dICsgcGxvdHNbWzRdXSAKICAgIGBgYAoKKiBDb25jbHVzw7VlczoKCiAgKiBPIHZhbG9yIGRlICRyJCBzw7MgcmVmbGV0ZSBjb3JyZWxhw6fDo28gW2xpbmVhcl17LmhsfS4KICAKICAqIEEgcHJlc2Vuw6dhIGRlIFsqb3V0bGllcnMqXXsuaGx9IHByb2R1eiB2YWxvcmVzIG7Do28tY29uZmnDoXZlaXMgZGUgJHIkLgogIAogIAojIyMgQ29ycmVsYcOnw6NvICRcbmVxJCBjYXVzYcOnw6NvIHstfQoKKiBEYWRvcyBjb2xldGFkb3MgZW0gT2xkZW5idXJnLCBuYSBBbGVtYW5oYSwgbm9zIGFub3MgJDE5MzAkLCBtb3N0cmFuZG8gYSBjb3JyZWxhw6fDo28gZW50cmUgYSBxdWFudGlkYWRlIGRlIGNlZ29uaGFzIGUgYSBwb3B1bGHDp8OjbyAoZGUgcGVzc29hcykgbmEgY2lkYWRlOgoKICAgIGBgYHtyIG1lc3NhZ2U9RkFMU0UsIGNhY2hlPVRSVUV9CiAgICBkZiA8LSByZWFkX2NzdignZGF0YS9TdG9ya3MuY3N2JykgJT4lIAogICAgICB0cmFuc211dGUoCiAgICAgICAgY2Vnb25oYXMgPSBTdG9ya3MsCiAgICAgICAgcGVzc29hcyA9IFBvcHVsYXRpb24KICAgICAgKSAKICAgIAogICAgciA8LSBjb3IoZGYkY2Vnb25oYXMsIGRmJHBlc3NvYXMpICU+JSByb3VuZCgyKQogICAgCiAgICBkZiAlPiUgCiAgICAgIGdncGxvdChhZXMoY2Vnb25oYXMsIHBlc3NvYXMpKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBsYWJzKAogICAgICAgICAgdGl0bGUgPSBwYXN0ZSgnciA9JywgcikKICAgICAgICApCiAgICAKICAgIGBgYAoKKiBWYXJpw6F2ZWwgb2N1bHRhOiBxdWFudGlkYWRlIGRlIGNhc2FzIGNvbSBjaGFtaW7DqS4KCiogT3UgY29pbmNpZMOqbmNpYTogaHR0cDovL3R5bGVydmlnZW4uY29tL3NwdXJpb3VzLWNvcnJlbGF0aW9ucwoKCiMgVGVzdGUgZSBpbnRlcnZhbG8gZGUgY29uZmlhbsOnYSBwYXJhIGEgY29ycmVsYcOnw6NvCgojIyBFeGVtcGxvOiBQSUIgZSBDTyRfMiQKCiogRW0gdW1hIGFtb3N0cmEgZGUgJDEwJCBwYcOtc2VzLCBleGFtaW5hbW9zIG8gUElCIChlbSB0cmlsaMO1ZXMgZGUgZMOzbGFyZXMpIGUgYSBxdWFudGlkYWRlIGRlIGVtaXNzw7VlcyBkZSBDTyRfMiQgKGVtIG1pbGjDtWVzIGRlIHRvbmVsYWRhcyk6CgogIGBgYHtyIGVjaG89RkFMU0V9CiAgZGYgPC0gdGliYmxlKAogICAgUElCID0gYygxLjcsIDEuMiwgMi41LCAyLjgsIDMuNiwgMi4yLCAwLjgsIDEuNSwgMi40LCA1LjkpLAogICAgZW1pc3PDtWVzID0gYygKICAgICAgNTUyLjYsIDQ2Mi4zLCA0NzUuNCwgMzc0LjMsIDc0OC41LCA0MDAuOSwgMjUzLjAsIDMxOC42LCA0OTYuOCwgMTE4MC42CiAgICApCiAgKQogIAogIGRmCiAgYGBgCgoqIEdyw6FmaWNvOgoKICAgIGBgYHtyfQogICAgZGYgJT4lIAogICAgICBnZ3Bsb3QoYWVzKFBJQiwgZW1pc3PDtWVzKSkgKwogICAgICAgIGdlb21fcG9pbnQoKSArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKAogICAgICAgICAgJ2VtaXNzw7Vlc1xuKG1pbGjDtWVzIGRlXG50b25lbGFkYXMpJywKICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCAxMjAwLCAyMDApLAogICAgICAgICAgbGltaXRzID0gYygwLCAxMjAwKQogICAgICAgICkgKwogICAgICAgIHNjYWxlX3hfY29udGludW91cygKICAgICAgICAgICdQSUIgKHRyaWxow7VlcyBVUyQpJywKICAgICAgICAgIGJyZWFrcyA9IDA6NiwKICAgICAgICAgIGxpbWl0cyA9IGMoMCwgNikKICAgICAgICApCiAgICBgYGAKCiogTyBjb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo28gW2Ftb3N0cmFsXXsuaGx9IMOpCgogICAgYGBge3J9CiAgICBjb3IoZGYkUElCLCBkZiRlbWlzc8O1ZXMpCiAgICBgYGAKCiogQ29tbyBlc3RhIMOpIHVtYSBhbW9zdHJhLCBkZXZlbW9zIGNhbGN1bGFyIHVtIGludGVydmFsbyBkZSBjb25maWFuw6dhIHBhcmEgbyBjb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo28gW3BvcHVsYWNpb25hbF17LmhsfS4KCiogTyBSIGZheiBpc3RvIGNvbSBhIGZ1bsOnw6NvIGBjb3IudGVzdGA6CgogICAgYGBge3J9CiAgICBjdCA8LSBjb3IudGVzdChkZiRQSUIsIGRmJGVtaXNzw7VlcykKICAgIGN0CiAgICBgYGAKCiogQ29uY2x1c8OjbzogY29tICQ5NVwlJCBkZSBjb25maWFuw6dhLCBlc3RpbWFtb3MgcXVlIG8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvIFtwb3B1bGFjaW9uYWxdey5obH0gw6kgY2FwdHVyYWRvIHBlbG8gaW50ZXJ2YWxvCgogICQkCiAgW1xxdWFkIGByIGN0JGNvbmYuaW50WzFdYCBccXVhZCA7XHF1YWQgIGByIGN0JGNvbmYuaW50WzJdYCBccXVhZF0KICAkJAoKIyMgRXhlcmPDrWNpb3MKCiogUXVhbCDDqSBvIHRpcG8gZGUgdGVzdGUgdXNhZG8gcG9yIGBjb3IudGVzdGA/CgoqIFF1YWlzIHPDo28gYXMgaGlww7N0ZXNlcyBkbyB0ZXN0ZT8KCiogQ29tbyDDqSBjYWxjdWxhZG8gbyB2YWxvciBkYSBlc3RhdMOtc3RpY2EgZG8gdGVzdGUgKCRgciBjdCRzdGF0aXN0aWMgJT4lIHJvdW5kKDQpYCQgbm8gZXhlbXBsbyBhY2ltYSk/CgoqIENvbW8gw6kgY2FsY3VsYWRvIG8gdmFsb3IgJHAkICgkYHIgY3QkcC52YWx1ZSAlPiUgcm91bmQoNylgJCBubyBleGVtcGxvIGFjaW1hKT8KCiogSW1wbGVtZW50ZSB1bWEgZnVuw6fDo28gZW0gUiBwYXJhIGZhemVyIG8gbWVzbW8gcXVlIGBjb3IudGVzdGAuCgoKIyBUcmFuc2Zvcm1hw6fDtWVzCgojIyBFeGVtcGxvOiBmb3RvZ3JhZmlhCgoqIEFvIGNvbmZpZ3VyYXIgdW1hIGPDom1lcmEgcGFyYSB0aXJhciB1bWEgZm90bywgdm9jw6ogZGV2ZSBhanVzdGFyIGEgdmVsb2NpZGFkZSBkbyBvYnR1cmFkb3IgZSBhIGFiZXJ0dXJhIGRvIGRpYWZyYWdtYS4KCiogTyBmYWJyaWNhbnRlIGRlIHVtYSBjw6JtZXJhIHJlY29tZW5kYSBvcyBzZWd1aW50ZXMgdmFsb3JlczoKCiAgYGBge3IgZWNobz1GQUxTRX0KICBkZiA8LSB0aWJibGUoCiAgICB2ZWxvY2lkYWRlID0gYygKICAgICAgMSAvIDEwMDAsCiAgICAgIDEgLyA1MDAsCiAgICAgIDEgLyAyNTAsCiAgICAgIDEgLyAxMjUsCiAgICAgIDEgLyA2MCwKICAgICAgMSAvIDMwLAogICAgICAxIC8gMTUsCiAgICAgIDEgLyA4CiAgICApLAogICAgYWJlcnR1cmEgPSBjKDIuOCwgNCwgNS42LCA4LCAxMSwgMTYsIDIyLCAzMikKICApCiAgCiAgZGYKICBgYGAKCiogQ29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvOgoKICAgIGBgYHtyfQogICAgY29yKGRmJHZlbG9jaWRhZGUsIGRmJGFiZXJ0dXJhKQogICAgYGBgCgoqIEdyw6FmaWNvOgoKICAgIGBgYHtyfQogICAgZGYgJT4lIAogICAgICBnZ3Bsb3QoYWVzKHZlbG9jaWRhZGUsIGFiZXJ0dXJhKSkgKwogICAgICAgIGdlb21fcG9pbnQoKQogICAgYGBgCgoqIFZhbW9zIHRyYW5zZm9ybWFyIG9zIHZhbG9yZXMgZGUgdW1hIGRhcyB2YXJpw6F2ZWlzIHBhcmEgdG9ybmFyIGEgY29ycmVsYcOnw6NvIG1haXMgbGluZWFyIChtZW5vcyBjdXJ2YSk6CgogICAgYGBge3J9CiAgICBkZiA8LSBkZiAlPiUgCiAgICAgIG11dGF0ZShxdWFkX2FiZXJ0dXJhID0gYWJlcnR1cmFeMikKICAgIGBgYAogICAgCiAgICBgYGB7cn0KICAgIGRmICU+JSAKICAgICAgZ2dwbG90KGFlcyh2ZWxvY2lkYWRlLCBxdWFkX2FiZXJ0dXJhKSkgKwogICAgICAgIGdlb21fcG9pbnQoKSArCiAgICAgICAgbGFicyh5ID0gJ2FiZXJ0dXJhwrInKQogICAgYGBgCgoqIEEgY29ycmVsYcOnw6NvIHNlIHRvcm5hCgogICAgYGBge3J9CiAgICBjb3IoZGYkdmVsb2NpZGFkZSwgZGYkcXVhZF9hYmVydHVyYSkKICAgIGBgYAoKIyMgRnVuw6fDtWVzIHVzYWRhcyBlbSB0cmFuc2Zvcm1hw6fDtWVzCgoqICRmKHgpID0geF4yJDogw7p0aWwgcXVhbmRvIG9zIHZhbG9yZXMgb3JpZ2luYWlzIHTDqm0gY2F1ZGEgbG9uZ2Egw6AgZXNxdWVyZGEsIG91IHF1YW5kbyBvcyB2YWxvcmVzIG9yaWdpbmFpcyB0w6ptIGNvbmNhdmlkYWRlIHBhcmEgYmFpeG8uCgoqICRmKHgpID0gXHNxcnR7eH0kOiDDunRpbCBwYXJhIHZhbG9yZXMgcXVlIHJlcHJlc2VudGFtIGNvbnRhZ2Vucy4KCiogJGYoeCkgPSBcbG9nIHgkOiDDunRpbCBwYXJhIHZhbG9yZXMgcG9zaXRpdm9zLCBxdWUgY3Jlc2NlbSBkZSBhY29yZG8gY29tIHBlcmNlbnRhZ2VucywgY29tbyBzYWzDoXJpb3MsIHBvcHVsYcOnw7VlcyBldGMuIENvbW8gJFxsb2cgMCQgbsOjbyDDqSBkZWZpbmlkbywgdm9jw6ogcG9kZSBwcmVjaXNhciBhY3Jlc2NlbnRhciB1bWEgY29uc3RhbnRlICRcdmFyZXBzaWxvbiQgYW9zIHZhbG9yZXMgYW50ZXMgZGEgdHJhbnNmb3JtYcOnw6NvLgoKKiAkZih4KSA9IC1cZnJhYzF7XHNxcnR7eH19JDogbyBzaW5hbCBuZWdhdGl2byBtYW50w6ltIGEgb3JkZW5hw6fDo28gZG9zIHZhbG9yZXMuCgoqICRmKHgpID0gLVxmcmFjMXgkOiDDunRpbCBwYXJhIHJhesO1ZXMsIGNvbW8ga20vaC4gTyBzaW5hbCBuZWdhdGl2byBtYW50w6ltIGEgb3JkZW5hw6fDo28gZG9zIHZhbG9yZXMuIENvbW8gJHkvMCQgbsOjbyDDqSBkZWZpbmlkbywgdm9jw6ogcG9kZSBwcmVjaXNhciBhY3Jlc2NlbnRhciB1bWEgY29uc3RhbnRlICRcdmFyZXBzaWxvbiQgYW9zIHZhbG9yZXMgYW50ZXMgZGEgdHJhbnNmb3JtYcOnw6NvLgoKCiMjIEV4ZXJjw61jaW8KCiogQXBsaXF1ZSBhcyBvdXRyYXMgdHJhbnNmb3JtYcOnw7VlcyBkYSBsaXN0YSBhY2ltYSBhb3MgdmFsb3JlcyBkYSB2YXJpw6F2ZWwgYGFiZXJ0dXJhYC4gRGVzZW5oZSBncsOhZmljb3MsIGNhbGN1bGUgY29ycmVsYcOnw7VlcywgZSBjb21wYXJlIG9zIHJlc3VsdGFkb3MuCgoKIyMgRXhlbXBsbzogcGxhbmV0YXMKCiogQSBkaXN0w6JuY2lhIG3DqWRpYSBkZSB1bSBwbGFuZXRhIGFvIFNvbCwgZW0gbWlsaMO1ZXMgZGUga20sIGVzdMOhIGNvcnJlbGFjaW9uYWRhIGNvbSBhIHBvc2nDp8OjbyBkbyBwbGFuZXRhIG5hIHNlcXXDqm5jaWEsIG1hcyBjb21vPwoKICBgYGB7ciBlY2hvPUZBTFNFfQogIHBsYW5ldGFzIDwtIHRyaWJibGUoCiAgICB+bm9tZSwgfnBvc2nDp8OjbywgfmRpc3TDom5jaWEsCiAgICAnTWVyY8O6cmlvJywgMSwgMzYsCiAgICAnVsOqbnVzJywgMiwgNjcsCiAgICAnVGVycmEnLCAzLCA5MywKICAgICdNYXJ0ZScsIDQsIDE0MiwKICAgICdKw7pwaXRlcicsIDUsIDQ4NCwKICAgICdTYXR1cm5vJywgNiwgODg3LAogICAgJ1VyYW5vJywgNywgMTc4NCwKICAgICdOZXR1bm8nLCA4LCAyNzk2LAogICAgJ1BsdXTDo28nLCA5LCAzNjY2CiAgKSAlPiUgCiAgICBtdXRhdGUoCiAgICAgIGRpc3TDom5jaWEgPSBkaXN0w6JuY2lhICogMS42CiAgICApCiAgCiAgcGxhbmV0YXMKICBgYGAKCiogQ29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvOgoKICAgIGBgYHtyfQogICAgY29yKHBsYW5ldGFzJHBvc2nDp8OjbywgcGxhbmV0YXMkZGlzdMOibmNpYSkKICAgIGBgYAoKKiBHcsOhZmljbzoKCiAgICBgYGB7cn0KICAgIHBsYW5ldGFzICU+JSAKICAgICAgZ2dwbG90KGFlcyhwb3Npw6fDo28sIGRpc3TDom5jaWEpKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTo5KSArCiAgICAgICAgbGFicygKICAgICAgICAgIHkgPSAnZGlzdMOibmNpYVxuKG1pbGjDtWVzIGttKScKICAgICAgICApCiAgICBgYGAKCiogVmFtb3MgdHJhbnNmb3JtYXIgb3MgdmFsb3JlcyBkYXMgZGlzdMOibmNpYXM6CgogICAgYGBge3J9CiAgICBwbGFuZXRhcyA8LSBwbGFuZXRhcyAlPiUgCiAgICAgIG11dGF0ZShsZGlzdCA9IGxvZzEwKGRpc3TDom5jaWEpKQogICAgYGBgCiAgICAKICAgIGBgYHtyfQogICAgcGxhbmV0YXMgJT4lIAogICAgICBnZ3Bsb3QoYWVzKHBvc2nDp8OjbywgbGRpc3QpKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTo5KSArCiAgICAgICAgbGFicygKICAgICAgICAgIHkgPSAnbG9nIGRpc3TDom5jaWFcbihtaWxow7VlcyBrbSknCiAgICAgICAgKQogICAgYGBgCgoqIEEgY29ycmVsYcOnw6NvIHNlIHRvcm5hCgogICAgYGBge3J9CiAgICBjb3IocGxhbmV0YXMkcG9zacOnw6NvLCBwbGFuZXRhcyRsZGlzdCkKICAgIGBgYAoKCiMjIEV4ZXJjw61jaW8KCiogQXBsaXF1ZSBhcyBvdXRyYXMgdHJhbnNmb3JtYcOnw7VlcyBkYSBsaXN0YSBhY2ltYSBhb3MgdmFsb3JlcyBkYSB2YXJpw6F2ZWwgYGRpc3TDom5jaWFgLiBEZXNlbmhlIGdyw6FmaWNvcywgY2FsY3VsZSBjb3JyZWxhw6fDtWVzLCBlIGNvbXBhcmUgb3MgcmVzdWx0YWRvcy4KCgoKCjxkaXYgc3R5bGU9J2hlaWdodDogMTAwMHB4Jz48L2Rpdj4K